/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       scroll_menu.h

	$Header: /game/scroll_menu.h $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "scroll_menu.h"

#include "adaptor_handler.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer_window.h"
#include "blend_window.h"
#include "bound_handler.h"
#include "font_cache.h"
#include "function_adaptors.h"
#include "game_time.h"
#include "idle_processor.h"
#include "mouse_event.h"
#include "pixel_24.h"
#include "standard_fonts.h"
#include "text_window.h"

static int const k_frame_size = 40;
static int const k_frame_time = 20;

static t_bitmap_group_cache  g_scroll_menu( "scroll_menu" );

namespace {

	class t_animator : public t_idle_processor
	{
	public:
		t_animator( t_scroll_menu* menu );

		virtual void on_idle();
	protected:
		t_scroll_menu * m_menu;
	};

}; // end unnamed namespace

// animate the opening.
t_animator::t_animator( t_scroll_menu* menu ) : m_menu(menu), 
                                                t_idle_processor(0,k_frame_time)
{
}

void t_animator::on_idle()
{
	m_menu->animate();
}

// --------------------------------------------------------
// menu that animates on open
// --------------------------------------------------------
t_scroll_menu::t_scroll_menu( t_window* parent )
             : t_window( k_completely_transparent, parent ), m_button_point(0,0)
{
	m_animator = 0;
	m_passing_event = false;
	m_bitmaps = g_scroll_menu.get();
	m_text_size = m_bitmaps->find( "text_size" )->get_rect();
	m_background_image = m_bitmaps->find( "background" );
	m_background = new t_window( t_screen_rect(0,0,0,0), k_completely_transparent, this );
	m_top_border = new t_bitmap_layer_window( m_bitmaps->find( "top_bar" ),
		                                      t_screen_point(0,0), this );
	m_button_point.x = m_text_size.left;

	int i;

	for (i = 0; i < m_bitmaps->size(); i++)
		if (strnicmp( (*m_bitmaps)[i].get_name().c_str(), "frame", 5 ) == 0)
		{
			m_bottom_frames.push_back( &(*m_bitmaps)[i] );
		}
}

t_scroll_menu::~t_scroll_menu()
{
	if (m_animator != 0)
		delete m_animator;
}

// --------------------------------------------------------
// menu that animates on open
// --------------------------------------------------------
void t_scroll_menu::add_item( std::string const& text, t_handler function, 
							  std::string const& help_text )
{
	t_button*      button = new t_button( m_button_point, m_background );
	t_cached_font  font = get_font( 20 );
	int            height = font->get_wrapped_height( text.c_str(), m_text_size.width() );
	t_screen_rect  rect( 0, 0, m_text_size.width(), height + 12 );
	t_screen_rect  text_rect( 0, 0, rect.right, rect.height() );
	t_text_window* released_window = new t_text_window( font, text_rect, button, text.c_str(),
		                                                t_pixel_24(0,0,0) );
	released_window->set_center_vertical();
	released_window->set_drop_shadow( false );

	t_blend_window* highlighted_window = new t_blend_window( t_pixel_24(0,0,200), 8, rect,
		                                                     button );
	t_text_window*  highlight_text = new t_text_window( font, text_rect, highlighted_window,
		                                                text.c_str(), t_pixel_24(255,255,255) );
	highlight_text->set_center_vertical();

	button->set_released_image( released_window );
	button->set_highlighted_image( highlighted_window );
	m_button_point.y += rect.height();

	button->set_click_handler( add_2nd_argument( 
		bound_handler( *this, &t_scroll_menu::button_click ), function) );
	button->set_help_balloon_text( help_text );
}

void t_scroll_menu::button_click( t_button* button, t_handler function )
{
	t_window_ptr window = this;

	close();
	m_passing_event = false;
	function();
}

// --------------------------------------------------------
// menu that animates on open
// --------------------------------------------------------
void t_scroll_menu::animate()
{
	if (!is_open())
		return;

	// compute how much of the background we should show.
	t_screen_rect rect = m_background->get_screen_rect();

	if (rect.height() >= m_final_height)
		return;

	// increase the height.
	rect.bottom += k_frame_size;
	if (rect.height() > m_final_height)
		rect.bottom = rect.top + m_final_height;

	// change the clipping rectangle.
	rect = to_client( rect );
	m_background->move( rect );

	// animate the scroll on the bottom.
	m_frame++;
	assert( m_frame < m_bottom_frames.size() );
	m_bottom_scroll->set_bitmap( m_bottom_frames[m_frame] );

	// move the scroll on the bottom.
	t_screen_rect bottom_rect = m_bottom_frames[m_frame]->get_rect();

	bottom_rect += t_screen_point( 0, rect.bottom - m_background_image->get_rect().bottom );
	m_bottom_scroll->move( bottom_rect );
}

// --------------------------------------------------------
// menu that animates on open
// --------------------------------------------------------
void t_scroll_menu::left_button_down( t_mouse_event const& event )
{
	if (m_passing_event)
		return;

	t_screen_point point = m_background->to_client( event.screen_point );

	if (!m_background->is_contained( point ))
	{
		// click outside of menu area.  close this menu and pass the click along.
		release_mouse();
		close();

		t_mouse_event new_event = event;

		t_window_ptr window = get_mouse_window( event.screen_point );
		
		if (window != 0)
		{
			new_event.client_point = window->to_client( event.screen_point );
			window->left_button_down( new_event );
		}
		return;
	}

	// we have the mouse captured, so send mouse messages down manually.
	t_window* window = m_background->get_child( point );

	if (window != 0)
	{
		t_mouse_event new_event = event;

		m_passing_event = true;
		new_event.client_point = window->to_client( event.screen_point );
		window->left_button_down( new_event );
		m_passing_event = false;
	}
}

// --------------------------------------------------------
// menu that animates on open
// --------------------------------------------------------
void t_scroll_menu::left_button_up( t_mouse_event const& event )
{
	// we have the mouse captured, so re-send all messages we get to the appropriate window.
	if (m_passing_event)
		return;

	t_screen_point point = t_window::get_modal_window()->to_client( event.screen_point );

	t_window* window = get_modal_window()->get_child( point );

	if (window != 0)
	{
		t_mouse_event new_event = event;

		m_passing_event = true;
		new_event.client_point = window->to_client( event.screen_point );
		window->left_button_up( new_event );
		m_passing_event = false;
	}
}

// --------------------------------------------------------
// menu that animates on open
// --------------------------------------------------------
void t_scroll_menu::mouse_move( t_mouse_event const& event )
{
	// we have the mouse captured, so re-send all messages we get to the appropriate window.
	if (m_passing_event)
		return;

	m_passing_event = true;

	t_screen_point point  = get_modal_window()->to_client( event.screen_point );
	t_window_ptr   window = get_modal_window()->get_child( point );

	if (window != m_last_mouse_window && m_last_mouse_window != 0)
	{
		t_mouse_event mouse_event = event;

		mouse_event.client_point = m_last_mouse_window->to_client( event.screen_point );
		m_last_mouse_window->mouse_leaving( m_last_mouse_window, 
			                                window, mouse_event );
	}

	m_last_mouse_window = window;
	if (window != 0)
	{
		t_mouse_event new_event = event;

		new_event.client_point = window->to_client( event.screen_point );
		window->mouse_move( event );
	}
	m_passing_event = false;
}

// --------------------------------------------------------
// menu that animates on open
// --------------------------------------------------------
void t_scroll_menu::open( t_screen_point origin )
{
	int start_height;

	// create bottom border.
	t_bitmap_layer const* layer;
	t_window*             child;
	t_screen_point        point;

	layer = m_bitmaps->find( "parchment" );
	point.x = 0;
	point.y = m_button_point.y - layer->get_rect().top;
	child = new t_bitmap_layer_window( layer, point, m_background );
	layer = m_bitmaps->find( "bottom_border" );
	point.y = m_button_point.y - layer->get_rect().top;
	child = new t_bitmap_layer_window( layer, point, m_background );

	// determine how large this will be when unfurled.
	m_final_height = m_button_point.y 
		             + m_bitmaps->find( "bottom_border_size" )->get_rect().height();
	// computer how large it is when first shown.
	int frames;

	start_height = m_final_height - (m_bottom_frames.size() - 1) * k_frame_size;
	if (start_height < 0)
		start_height = 0;
	frames = (m_final_height - start_height + k_frame_size - 1) / k_frame_size;
	m_frame = m_bottom_frames.size() - 1 - frames;
	assert( m_frame >= 0 );

	// create images for the background.
	t_bitmap_layer const* left = m_bitmaps->find( "left" );
	t_bitmap_layer const* right = m_bitmaps->find( "right" );
	int            right_edge = right->get_rect().right;
	t_screen_rect  clipper_rect( 0, 0, right_edge, m_button_point.y );
	t_window*      clipper = new t_window( clipper_rect, k_completely_transparent, 
		                                   m_background );
	t_screen_rect  background_rect = m_background_image->get_rect();
	t_screen_point offset( 0, -background_rect.top );

	point.x = 0;
	point.y = 0;
	clipper->move_to_back();

	while (point.y < m_final_height)
	{
		child = new t_bitmap_layer_window( left, point + offset, clipper );
		child = new t_bitmap_layer_window( right, point + offset, clipper );
		child = new t_bitmap_layer_window( m_background_image, point + offset, clipper );
		point.y += background_rect.height();
	}
	// size the background window appropriately.
	background_rect.left = 0;
	background_rect.right = right_edge;
	background_rect.bottom = background_rect.top + start_height;
	m_background->move( background_rect );

	// create the bottom bar.
	m_bottom_scroll = new t_bitmap_layer_window( m_bottom_frames[m_frame],
		                                         t_screen_point(0,0), this, false );

	// move the scroll on the bottom.
	t_screen_rect bottom_rect = m_bottom_frames[m_frame]->get_rect();

	bottom_rect += t_screen_point( 0, background_rect.bottom - m_background_image->get_rect().bottom );
	m_bottom_scroll->move( bottom_rect );

	// compute the overall size of the menu.
	t_screen_rect rect = m_top_border->get_screen_rect();

	rect = get_extent( rect, background_rect );
	point.x = 0;
	point.y = background_rect.top + m_final_height - m_background_image->get_rect().bottom;
	rect = get_extent( rect, m_bottom_frames.back()->get_rect() + point );
	rect -= rect.top_left();

	// make top right of m_top_bar correspond to "point"
	rect -= t_screen_point( m_top_border->get_width(), 0 );
	rect += origin;

	init( rect );
	fit_to_parent_rect();
	capture_mouse();
	m_animator = new t_animator( this );
}
